package com.guqj.basis.proxy.jdk;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * <p>
 * 		通过代理类来控制对真实对象的访问
 * </p>
 * <p>
 * 	动态代理类注意点：
 *     1) 如果所代理的接口都是 public的，那么它将被定义在顶层包（即包路径为空），如果所代理的接口中有非 public 的接口（因为接口不能被定义为 protect 或 private，所以除 public 之外就是默认的 package 访问级别），<br>
 *     那么它将被定义在该接口所在包,这样设计的目的是为了最大程度的保证动态代理类不会因为包管理的问题而无法被成功定义并访问<br>
 *     2) 生成的代理类为public final，不能被继承<br>
 *     3) 类名：格式是“$ProxyN”，N是逐一递增的数字，代表Proxy被第N次动态生成的代理类，要注意，对于同一组接口(接口的排列顺序也相同)，不会重复创建动态代理类，而是返回一个先前已经创建并缓存了的代理类对象。提高了效率。<br>
 *     4) Proxy 类是它的父类，这个规则适用于所有由 Proxy 创建的动态代理类。(也算是java动态代理的一处缺陷，java不支持多继承，所以无法实现对class的动态代理，只能对于Interface的代理)而且该类还实现了其所代理的一组接口，这就是为什么它能够被安全地类型转换到其所代理的某接口的根本原因。<br>
 *     5) 代理类的根类 java.lang.Object 中有三个方法也同样会被分派到调用处理器的 invoke 方法执行，它们是 hashCode，equals 和 toString
 * </p>
 * 
 * <p>
 * 	代理机制及其特点
 *	1. 通过实现 @see InvocationHandler 接口创建自己的调用处理器；
 *	2. 通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类；
 *	3. 通过反射机制获得动态代理类的构造函数，其唯一参数类型是调用处理器接口类型；
 *	4. 通过构造函数创建动态代理类实例，构造时调用处理器对象作为参数被传入。
 *  5. JDK的动态代理用起来非常简单，但它有一个限制，就是使用动态代理的对象必须实现一个或多个接口
 * </p>
 * @author guqj
 * @date   2017年3月10日
 */
public class HeroProxy implements InvocationHandler {

	private Object obj;

	/**
	 * proxy 指最终生成的代理对象
	 * method 指代的是我们所要调用真实对象的某个方法的Method对象
	 * args 指代的是调用真实对象某个方法时接受的参数
	 */
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		// 在代理真实对象前我们可以添加一些自己的操作
		System.out.println("proxy class:" + proxy.getClass().getName());
		System.out.println("Method: " + method);
		// 当代理对象调用真实对象的方法时，其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用, 利用反射执行方法
		return method.invoke(obj, args);
	}

	/**  
     * 绑定委托对象并返回一个【代理占位】 
     * <p>Proxy类与普通类的唯一区别就是其字节码是由 JVM 在运行时动态生成的而非预存在于任何一个 .class 文件中</p>
     * @param target 真实对象 
     * @return  代理对象【占位】 
     */ 
	public Object bind(Object target, Class<?>[] interfaces){
		this.obj = target;
		return Proxy.newProxyInstance(target.getClass().getClassLoader(), interfaces, this);
	}
}
